package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.UserController;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.example.lotterysystem.controller.param.CreateUserByActivityParam;
import com.example.lotterysystem.controller.param.PageParam;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.UserService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {
    private final static Logger logger= LoggerFactory.getLogger(ActivityServiceImpl.class);

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 设置缓存key的前缀
     */
    private final String ACTIVITY_PREFIX="ACTIVITY_";
    /**
     * 设置缓存过期时间，3天
     */
    private final Long ACTIVITY_TIMEOUT=60*60*24*3L;


    /**
     * 创建活动
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)//涉及多表
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //效验活动信息是否正确
        checkActivityInfo(param);

        //保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
activityMapper.insert(activityDO);

        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
List<ActivityPrizeDO>activityPrizeDOList=prizeParams.stream()
        .map(prizeParam -> {
            ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
            activityPrizeDO.setActivityId(activityDO.getId());
            activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
            activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
            activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
            return activityPrizeDO;
        }).collect(Collectors.toList());
activityPrizeMapper.BatchInsert(activityPrizeDOList);

        //保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO>activityUserDOList=userParams.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.BatchInsert(activityUserDOList);
        //整合完整的活动信息，存放redis
//通过activityId获取ActivityDetailDTO:活动+奖品+人员
        //先获取奖品基本属性列表
        //获取需要查询的奖品id列表
        List<Long> prizeIds=param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct().collect(Collectors.toList());
        List<PrizeDO>prizeDOList=prizeMapper.batchSelectByIds(prizeIds);


        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO, activityPrizeDOList
                , activityUserDOList, prizeDOList);
        //将数据存入redis
        cacheActivity(activityDetailDTO);
        //构造返回
CreateActivityDTO createActivityDTO=new CreateActivityDTO();
createActivityDTO.setActivityId(activityDO.getId());
return createActivityDTO;
    }


    /**
     * 翻页查询活动列表
     * @param param
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取总量
        int total = activityMapper.count();

        //获取当前页列表
        List<ActivityDO> activityDOList = activityMapper.selectByPage(param.offset(),param.getPageSize());
List<ActivityDTO>activityDTOList=activityDOList.stream()
        .map(activityDO -> {
            ActivityDTO activityDTO = new ActivityDTO();
            activityDTO.setActivityId(activityDO.getId());
            activityDTO.setActivityName(activityDO.getActivityName());
            activityDTO.setActivityDesc(activityDO.getDescription());
            activityDTO.setStatus(ActivityStatusEnum.valueOf(activityDO.getStatus()));
return activityDTO;
        }).collect(Collectors.toList());

        return new PageListDTO<>(total,activityDTOList);
    }

    /**
     * 获取活动详情
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null==activityId){
            logger.warn("要查询的活动id为空！");
            return null;
        }

        //从redis中获取活动信息
        ActivityDetailDTO activityDetailDTO = getActivityDetailDTO(activityId);
        if(activityDetailDTO!=null){
            logger.info("从redis中获取活动信息成功！key:{}",ACTIVITY_PREFIX+activityId);
            return activityDetailDTO;
        }

        //如果redis中没有，则从数据库中获取，并缓存到redis中
        //活动表
        ActivityDO activityDO=activityMapper.selectById(activityId);

        //活动奖品表
        List<ActivityPrizeDO>activityPrizeDOList=activityMapper.selectActivityPrizeList(activityId);
        //活动人员表
        List<ActivityUserDO>activityUserDOList=activityMapper.selectActivityUserList(activityId);
        //奖品表:先获取要查询的奖品id
        List<Long>prizeIds=activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
activityDetailDTO=convertToActivityDetailDTO(activityDO,activityPrizeDOList,activityUserDOList,prizeDOList);

        //整合完整活动信息，存放redis中
cacheActivity(activityDetailDTO);
        return activityDetailDTO;
    }

    /**
     * 扭转活动状态
     * @param activityId
     */
    @Override
    public void cacheActivity(Long activityId) {
        if(activityId==null){
            logger.warn("要缓存的活动id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //查询表数据：活动表，关联奖品，关联人员，奖品信息表

        //查询表数据
        //如果redis中没有，则从数据库中获取，并缓存到redis中
        //活动表
        ActivityDO activityDO=activityMapper.selectById(activityId);
        if(activityDO==null){
            logger.warn("要缓存的活动不存在！id:{}",activityId);
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_IS_NOT_EXIST);
        }

        //活动奖品表
        List<ActivityPrizeDO>activityPrizeDOList=activityMapper.selectActivityPrizeList(activityId);
        //活动人员表
        List<ActivityUserDO>activityUserDOList=activityMapper.selectActivityUserList(activityId);
        //奖品表:先获取要查询的奖品id
        List<Long>prizeIds=activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合完整活动信息，存放redis中
        cacheActivity(convertToActivityDetailDTO(activityDO,
                activityPrizeDOList,activityUserDOList,prizeDOList));

    }

    private ActivityDetailDTO getActivityDetailDTO(Long activityId) {
        if(activityId==null){
            logger.warn("要查询的活动id为空！");
            return null;
        }
        try{
            String activityDetailJson = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(activityDetailJson)) {
                logger.warn("从redis中获取活动信息失败！key:{}",ACTIVITY_PREFIX+activityId);
                return null;
            }
            return JacksonUtil.readValue(activityDetailJson, ActivityDetailDTO.class);
        }catch (Exception e){
            logger.error("从redis中获取活动信息失败！key:{}",ACTIVITY_PREFIX+activityId,e);
            return null;
        }

    }

    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
//key:ACTIVITY_12
        //value:activityDetailDTO(json)
        if(activityDetailDTO==null||activityDetailDTO.getActivityId()==null){
logger.warn("要缓存的活动信息不存在！");
return;
        }
        try{
            redisUtil.set(ACTIVITY_PREFIX+activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e) {
            logger.error("缓存活动信息失败！",JacksonUtil.writeValueAsString(activityDetailDTO) ,e);
        }
    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList) {
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

List<ActivityDetailDTO.PrizeDTO>prizeDTOList=activityPrizeDOList.stream()
                .map(activityPrizeDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO ->
                                    prizeDO.getId().equals(activityPrizeDO.getPrizeId()))
                            .findFirst();
optionalPrizeDO.ifPresent(prizeDO -> {
    prizeDTO.setName(prizeDO.getName());
    prizeDTO.setPrice(prizeDO.getPrice());
    prizeDTO.setImageUrl(prizeDO.getImageUrl());
    prizeDTO.setDescription(prizeDO.getDescription());
});
prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(activityPrizeDO.getPrizeTiers()));
prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
activityDetailDTO.setPrizeDTOList(prizeDTOList);

List<ActivityDetailDTO.UserDTO>userDTOList=activityUserDOList.stream()
        .map(activityUserDO -> {
            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
            userDTO.setUserId(activityUserDO.getUserId());
            userDTO.setUserName(activityUserDO.getUserName());
            userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));
            return userDTO;
        }).collect(Collectors.toList());
activityDetailDTO.setUserDTOList(userDTOList);
return activityDetailDTO;
    }

    private void checkActivityInfo(CreateActivityParam param) {
        if(null==param){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        //人员id是否在人员表中存在
        List<Long> userIds=param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct().collect(Collectors.toList());
                List<Long> existUserIds=userMapper.selectExitsByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.User_INFO_IS_EMPTY);
        }
userIds.forEach(id->{
    if(!existUserIds.contains(id)){
        throw new ServiceException(ServiceErrorCodeConstants.User_INFO_IS_EMPTY);
    }
});


        //奖品id是否在奖品表中存在
        List<Long> prizeIds=param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct().collect(Collectors.toList());
        List<Long> existPrizIds=prizeMapper.selectExitsByIds(prizeIds);
        if (CollectionUtils.isEmpty(existPrizIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.Prize_INFO_IS_EMPTY);
        }

        prizeIds.forEach(id->{
            if(!existPrizIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.Prize_INFO_IS_EMPTY);
            }
        });


        //人员数量是否大于等于奖品数量
int userCount=param.getActivityUserList().size();
long prizeCount=param.getActivityPrizeList()
        .stream()
        .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
        .sum();
if(userCount<prizeCount){
    throw new ServiceException(ServiceErrorCodeConstants.USER_COUNT_IS_NOT_ENOUGH);
}
//效验活动奖品等级是否有效
        param.getActivityPrizeList().forEach(prizeParam -> {
            if(null==ActivityPrizeTiersEnum.forName(prizeParam.getPrizeTiers())){
                throw new ServiceException(ServiceErrorCodeConstants.PRIZE_TIER_IS_INVALID);
            }
        });



    }
}
